1 module hip.hipaudio.backend.sles;
2 import hip.error.handler;
3 import hip.console.log;
4 import hip.util.conv:to;
5 import core.atomic;
6 
7 version(Android):
8 import opensles.android;
9 import opensles.sles;
10 /**
11 *   OpenSL ES Debuggability
12 */
13 package __gshared SLresult[] sliErrorQueue;
14 package __gshared string[]   sliErrorMessages;
15 
16 ///Packs engine interface, object and capabilities and give a cleaner interface for use
17 struct SLIEngine
18 {
19     SLObjectItf engineObject = null;
20     SLEngineItf engine;
21     SLEngineCapabilitiesItf engineCapabilities;
22     bool willUseFastMixer;
23 
24     void initialize()
25     {
26         SLEngineOption engineOptions = SLEngineOption(SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE);
27         sliCall(slCreateEngine(&engineObject,1,&engineOptions,0,null,null),
28         "Could not create engine");
29 
30         //Initialize|Realize the engine
31         sliCall((*engineObject).Realize(engineObject, SL_BOOLEAN_FALSE),
32         "Could not realize|initialize engine");
33         
34 
35         //Get the interface for being able to create child objects from the engine
36         sliCall((*engineObject).GetInterface(engineObject, SL_IID_ENGINE, &engine),
37         "Could not get an interface for creating objects");
38 
39         if(sliCall((*engineObject).GetInterface(engineObject, SL_IID_ENGINECAPABILITIES, &engineCapabilities),
40         "Could not get engine capabilities"))
41         {
42             if(sliCall((*engineCapabilities).QueryAPIVersion(engineCapabilities, &engineMajor, &engineMinor, &enginePatch),
43             "Could not query OpenSLES version"))
44                 rawlog("OpenSL Version: "~to!string(engineMajor)~"."~to!string(engineMinor)~"."~to!string(enginePatch));
45             else if(sliErrorMessages.length == 1)
46                 sliClearErrors();
47         }
48         else if(sliErrorMessages.length == 1)
49             sliClearErrors();
50     }
51 
52     uint CreateOutputMix(const(const(SLObjectItf_)*)** outputMix, uint interfacesCount,
53     const(const(SLInterfaceID_)*)* interfaces, const(uint)* requirements)
54     {
55         return (*engine).CreateOutputMix(engine, outputMix, interfacesCount, interfaces, requirements);
56     }
57     uint CreateAudioPlayer(SLObjectItf* audioPlayer, SLDataSource* source, SLDataSink* sink,
58     uint interfacesCount, SLInterfaceID* interfaces, uint* requirements)
59     {
60         return (*engine).CreateAudioPlayer(engine, audioPlayer, source, sink, interfacesCount, interfaces, requirements);
61     }
62 
63     void Destroy()
64     {
65         if(engineObject != null)
66         {
67             (*engineObject).Destroy(engineObject);
68         }
69     }
70 }
71 
72 /**
73 *   Engine related objects
74 */
75 package __gshared SLIEngine engine; 
76 package __gshared short engineMajor = 1;
77 package __gshared short engineMinor = 0;
78 package __gshared short enginePatch = 1;
79 
80 /**
81 *   Controls the output and the players
82 */
83 package __gshared SLIOutputMix outputMix;
84 package __gshared SLIAudioPlayer*[] genPlayers;
85 
86 
87 
88 string sliGetError(SLresult res)
89 {
90     switch (res)
91     {
92         case SL_RESULT_SUCCESS: return "Success";
93         case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
94         case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
95         case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
96         case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
97         case SL_RESULT_CONTROL_LOST: return "Control lost";
98         case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
99         case SL_RESULT_INTERNAL_ERROR: return "Internal error";
100         case SL_RESULT_IO_ERROR: return "IO error";
101         case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
102         case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
103         case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
104         case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
105         case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
106         case SL_RESULT_RESOURCE_ERROR: return "Resource error";
107         case SL_RESULT_RESOURCE_LOST: return "Resource lost";
108         case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
109         default: return "Undefined error";
110     }
111 }
112 
113 package void sliClearErrors()
114 {
115     sliErrorMessages.length = 0;
116     sliErrorQueue.length = 0;
117 }
118 
119 
120 bool sliError(SLresult res, lazy string errMessage, string file = __FILE__, string func = __PRETTY_FUNCTION__,  uint line = __LINE__)
121 {
122     if(res != SL_RESULT_SUCCESS)
123     {
124         sliErrorQueue~= res;
125         rawerror("'OpenSL ES' Error: '"~sliGetError(res)~"' at file "~file~":"~to!string(line)~ " at "~func~"\n\t"~errMessage);
126     }
127     return res != SL_RESULT_SUCCESS;
128 }
129 
130 /**
131 *   Calls OpenSL ES function, checks if there was error, and append the message on the messagesqueue
132 * when an error occurs
133 *
134 *   Returns wether the call was okay
135 */
136 private bool sliCall(SLresult opRes, string errMessage,
137 string file = __FILE__, string func = __PRETTY_FUNCTION__,  uint line = __LINE__)
138 {
139     if(sliError(opRes, errMessage, file, func, line))
140     {
141         sliErrorMessages~= errMessage;
142         return false;
143     }
144 
145     return true;
146 }
147 
148 
149 struct SLIOutputMix
150 {
151     SLEnvironmentalReverbItf environmentReverb;
152     SLPresetReverbItf presetReverb;
153     SLBassBoostItf bassBoost;
154     SLEqualizerItf equalizer;
155     SLVirtualizerItf virtualizer;
156     SLObjectItf outputMixObj;
157 
158 
159     static bool initializeForAndroid(ref SLIOutputMix output, ref SLIEngine e, bool willUseFastMixer)
160     {
161         //All those interfaces are supported on Android, so, require them
162         const(SLInterfaceID)* ids = null;
163         const(SLboolean)* req = null;
164         uint count = 0;
165 
166         if(!willUseFastMixer)
167         {
168             ids = 
169             [
170                 SL_IID_ENVIRONMENTALREVERB,
171                 SL_IID_PRESETREVERB,
172                 SL_IID_BASSBOOST,
173                 SL_IID_EQUALIZER,
174                 SL_IID_VIRTUALIZER
175             ].ptr;
176 
177             req = 
178             [
179                 SL_BOOLEAN_TRUE,
180                 SL_BOOLEAN_TRUE,
181                 SL_BOOLEAN_TRUE,
182                 SL_BOOLEAN_TRUE,
183                 SL_BOOLEAN_TRUE //5
184             ].ptr;
185             count = 5;
186         }
187 
188         with(output)
189         {
190             sliCall(e.CreateOutputMix(&outputMixObj, count, ids, req),
191             "Could not create output mix");
192             //Do it assyncly
193             sliCall((*outputMixObj).Realize(outputMixObj, SL_BOOLEAN_FALSE),
194             "Could not initialize output mix");
195 
196 
197             if(!willUseFastMixer)   
198             {
199                 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_ENVIRONMENTALREVERB, &environmentReverb),
200                 "Could not get the ENVIRONMENTALREVERB interface"))
201                     environmentReverb = null;
202 
203                 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_PRESETREVERB, &presetReverb),
204                 "Could not get the PRESETREVERB interface"))
205                     presetReverb = null;
206                 
207                 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_BASSBOOST, &bassBoost),
208                 "Could not get the BASSBOOST interface"))
209                     bassBoost = null;
210 
211                 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_EQUALIZER, &equalizer),
212                 "Could not get the EQUALIZER interface"))
213                     equalizer = null;
214 
215                 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_VIRTUALIZER, &virtualizer),
216                 "Could not get the VIRTUALIZER interface"))
217                     virtualizer = null;
218             }
219         }
220         return sliErrorQueue.length == 0;
221     }
222 }
223 
224 
225 float sliToAttenuation(float gain)
226 {
227     import std.math:log10;
228     return (gain < 0.01f) ? -96.0f : 20 * log10(gain);
229 }
230 
231 const(SLInterfaceID)[] getAudioPlayerInterfaces(bool willUseFastMixer)
232 {
233     const(SLInterfaceID)[] ret = [SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE/*, SL_IID_MUTESOLO*/]; //can't require SL_IID_MUTESOLO with a mono buffer queue data source error
234     if(!willUseFastMixer)
235     {
236         ret~= [
237             SL_IID_BASSBOOST,
238             SL_IID_EFFECTSEND,
239             SL_IID_ENVIRONMENTALREVERB,
240             SL_IID_EQUALIZER,
241             SL_IID_PLAYBACKRATE,
242             SL_IID_PRESETREVERB,
243             SL_IID_VIRTUALIZER, 
244             SL_IID_ANDROIDEFFECT,
245             SL_IID_ANDROIDEFFECTSEND, 
246             SL_IID_METADATAEXTRACTION
247         ][];
248     }
249     return ret;
250 }
251 SLboolean[] getAudioPlayerRequirements(ref const(SLInterfaceID)[] itfs)
252 {
253     SLboolean[] ret;
254     foreach (const(SLInterfaceID) id; itfs)
255         ret~= SL_BOOLEAN_TRUE;
256     return ret;
257 }
258 
259 string sliGetErrorMessages()
260 {
261     string ret;
262     for(int i = 0; i < sliErrorMessages.length;i++)
263         ret~= sliGetError(sliErrorQueue[i])~": "~sliErrorMessages[i];
264 
265     sliErrorQueue.length = 0;
266     sliErrorMessages.length = 0;
267     return ret;
268 }
269 
270 ///Must be used as an opaque pointer
271 struct SLIBuffer
272 {
273     size_t size;
274     bool isLocked;
275     bool hasBeenProcessed;
276     ///Tightly packed structure
277     void[0] data;
278 }
279 
280 /**
281 *   Creates an unresizable buffer(tightly packed) for not getting a cache miss
282 */
283 SLIBuffer* sliGenBuffer(void[] data, size_t size)
284 {
285     import core.stdc.stdlib:malloc;
286     import core.stdc.string:memcpy,memset;
287     SLIBuffer* buf = cast(SLIBuffer*)malloc(SLIBuffer.sizeof+size);
288     buf.size = size;
289     buf.isLocked = false;
290     buf.hasBeenProcessed = false;
291     if(data == null)
292         memset(buf.data.ptr, 0, size);
293     else
294         memcpy(buf.data.ptr, data.ptr, size);
295     return buf;
296 }
297 
298 ///Copies data inside the buffer on its immutable size. Use that on unlocked buffers.
299 void sliBufferData(SLIBuffer* buffer, void[] data)
300 {
301     import core.stdc.string:memcpy;
302     ErrorHandler.assertExit(!buffer.isLocked, "Can't write to locked buffer");
303     memcpy(buffer.data.ptr, data.ptr, buffer.size);
304 }
305 
306 ///Invalidates the buffer and makes it null
307 void sliDestroyBuffer(ref SLIBuffer* buff)
308 {
309     import core.stdc.stdlib:free;
310     if(buff != null)
311         free(buff);
312     buff = null;
313 }
314 
315 __gshared struct SLIAudioPlayer
316 {
317     ///The Audio player
318     SLObjectItf playerObj;
319     ///Play/stop/pause the audio
320     SLPlayItf player;
321     ///Controls the volume
322     SLVolumeItf playerVol;
323     ///Ability to get and set the audio duration
324     SLSeekItf playerSeek;
325 
326     SLBassBoostItf bassBoost;
327     SLEnvironmentalReverbItf envReverb;
328     SLEqualizerItf equalizer;
329     SLPlaybackRateItf playbackRate;
330     SLPresetReverbItf presetReverb;
331     SLVirtualizerItf virtualizer;
332     SLAndroidEffectItf androidEffect;
333     SLAndroidEffectSendItf androidEffectSend;
334 
335     ///TODO:
336     SLEffectSendItf playerEffectSend;
337     ///TODO:
338     SLMetadataExtractionItf playerMetadata;
339 
340     struct EnqueuedBuffer
341     {
342         void* data;
343         size_t size;
344     }
345     
346     EnqueuedBuffer enqueued;
347 
348     /**
349     *   This queue works as:
350 
351         In Use   Unqueued    Capacity
352     |1, 2, 3, 4|  /5, 6\   (0, 0, 0, 0)
353     */
354     protected SLIBuffer** streamQueue;
355     ///Data between | |
356     protected ushort streamQueueLength;
357     ///Data between / \
358     protected ushort streamQueueFree;
359     //Data between ( )
360     protected ushort streamQueueCapacity;
361     ///How many chunks have been streamed to that player
362     protected ushort totalChunksEnqueued;
363     ///How many chunks have been played
364     protected ushort totalChunksPlayed;
365 
366 
367     SLAndroidSimpleBufferQueueItf playerAndroidSimpleBufferQueue;
368     SLIBuffer* nextBuffer;
369     bool isPlaying, hasFinishedTrack, isLooping;
370 
371     float volume;
372 
373     void update()
374     {
375         if(nextBuffer != null)
376         {
377             //Need to clear queue, as having too many can cause a crash
378             SLIAudioPlayer.Clear(this);
379             //Set it playing
380             SLIAudioPlayer.Enqueue(this, nextBuffer.data.ptr, nextBuffer.size);
381             nextBuffer = null;
382         }
383         else if(hasFinishedTrack)
384         {
385             if(isLooping)
386             {
387                 if(enqueued != EnqueuedBuffer.init)
388                 {
389                     SLIAudioPlayer.stop(this);
390                     SLIAudioPlayer.Enqueue(this, enqueued.data, enqueued.size);
391                     SLIAudioPlayer.play(this);
392                 }
393                 else
394                     loglnError("Tried to loop OpenSLES AudioPlayer, but there is no enqueued buffer");
395             }
396             else
397             {
398                 // logln("STOPPED!");
399                 SLIAudioPlayer.stop(this);
400             }
401         }            
402     }
403 
404     static void setVolume(ref SLIAudioPlayer audioPlayer, float gain)
405     {
406         with(audioPlayer)
407         {
408             (*playerVol).SetVolumeLevel(playerVol, cast(SLmillibel)(sliToAttenuation(gain)*100));
409             volume = gain;
410         }
411     }
412 
413     static void setRate(ref SLIAudioPlayer audioPlayer, float rate)
414     {
415         if(rate < 0.5 || rate > 2.0)
416         {
417             import hip.util.conv:to;
418             ErrorHandler.showErrorMessage("Unsupported rate change on OpenSL ES.", "Max rate change is from 0.5 to 2.0, received "~rate.to!string);
419         }
420         short newRate = cast(short)(rate * 1000);
421         with(audioPlayer)
422         {
423             sliCall((*playbackRate).SetRate(playbackRate, newRate), "Could not set playback rate");
424         }
425     }
426 
427     /**
428     *   Also invalidates enqueued SLIBuffer, so, destroy with care
429     */
430     static void destroyAudioPlayer(ref SLIAudioPlayer audioPlayer)
431     {
432         with(audioPlayer)
433         {
434             (*playerObj).Destroy(playerObj);
435             if(streamQueue != null)
436             {
437                 for(int i = 0; i < streamQueueLength; i++)
438                     sliDestroyBuffer(streamQueue[i]);
439                 streamQueue = null;
440                 streamQueueLength = 0;
441             }
442             playerObj = null;
443             player = null;
444             playerVol = null;
445             playerSeek = null;
446             playerEffectSend = null;
447             playerAndroidSimpleBufferQueue = null;
448         }
449     }
450     alias PlayerCallback = extern(C) void function(SLPlayItf player, void* context, SLuint32 event);
451 
452     void RegisterCallback(PlayerCallback callback, void* context)
453     {
454         (*player).RegisterCallback(player, callback, context);
455     }
456     void SetCallbackEventsMask(uint mask)
457     {
458         (*player).SetCallbackEventsMask(player, mask);
459     }
460 
461     /**
462     *   Checking some bug issues tracker: 
463     *   - https://groups.google.com/g/android-ndk/c/zANdS2n2cQI
464     *
465     *   - https://issuetracker.google.com/issues/37011991
466     *
467     *   It seems that you can't make any call to OpenSL API inside SL_PLAYEVENT_HEADATEND
468     *
469     *   I'm delegating it right now to the void update() method. As it seems, there is a little
470     *   more music playing after SL_PLAYEVENT_HEADATEND(it is notified early).
471     *
472     *   It can cause some unsync in some other device, but that must be checked case-to-case.
473     *   
474     *   Using ushort.max seems to be the way to go about how much it need to decode. ushort.max/4 caused some
475     *   interruptions in music, while ushort.max/8 was inaudible (ushort.max is a great number, 65k)
476     *
477     *   It is also possible to bypass the need for a callback by calling GetState and checking if count == 0,
478     *   that will mean that the head is at the end
479     */
480     extern(C) static void checkStreamCallback(SLPlayItf player, void* context, SLuint32 event)
481     {
482         if(event & SL_PLAYEVENT_HEADATEND)
483         {
484             import hip.console.log;
485             SLIAudioPlayer* p = (cast(SLIAudioPlayer*)context);
486             if(p.streamQueueLength > 0)
487             {
488                 SLIBuffer* buf;
489                 if(p.totalChunksPlayed == 0)
490                     buf = p.streamQueue[0];
491                 else
492                 {
493                     p.unqueue(p.streamQueue[0]); //Unqueue the old buffer, thus, streamQueueLength--
494                     if(p.streamQueueLength > 0)
495                         buf = p.streamQueue[0];
496                 }
497                 if(buf != null)
498                 {
499                     buf.isLocked = true;
500                     p.totalChunksPlayed++;
501                     p.nextBuffer = buf;
502                 }
503             }
504             else
505             {
506                 import hip.console.log;
507                 logln("Finished track on AudioThread");
508                 p.hasFinishedTrack = true;
509             }
510         }
511     }
512 
513     void pushBuffer(SLIBuffer* buffer)
514     {
515         import core.stdc.stdlib:malloc,realloc;
516 
517         totalChunksEnqueued++;
518         streamQueueLength++;
519         ushort totalSize = cast(ushort)(streamQueueLength + streamQueueFree);
520 
521         if(streamQueue == null)
522             streamQueue = cast(SLIBuffer**)malloc((SLIBuffer*).sizeof * totalSize);
523         else if(totalSize > streamQueueCapacity)
524         {
525             streamQueue = cast(SLIBuffer**)realloc(streamQueue, (SLIBuffer*).sizeof * totalSize);
526             streamQueueCapacity = totalSize;
527         }
528         buffer.hasBeenProcessed = false;
529         //Buffer is locked when playing
530         streamQueue[streamQueueLength-1] = buffer;
531     }
532 
533     /**
534     * Gets a free buffer from the /\ list
535     */
536     SLIBuffer* getProcessedBuffer()
537     {
538         for(int i = streamQueueLength; i < streamQueueLength+streamQueueFree;i++)
539         {
540             if(!streamQueue[i].isLocked && streamQueue[i].hasBeenProcessed)
541                 return streamQueue[i];
542         }
543         return null;
544     }
545     /**
546     *   Will remove the free buffer and set it as unused /b\ -> (0)
547     *
548     * - | | = Enqueued
549     *
550     * - / \ = Free
551     *
552     * - ( ) = Unused (capacity)
553     */
554     void removeFreeBuffer(SLIBuffer* freeBuffer)
555     {
556         ErrorHandler.assertExit(!freeBuffer.isLocked, "This buffer is being used right now");
557         bool isReordering = false;
558         for(int i = streamQueueLength; i < streamQueueLength+streamQueueFree;i++)
559         {
560             if(streamQueue[i] == freeBuffer)
561                 isReordering = true;
562             if(isReordering && i+1 < streamQueueCapacity)
563                 streamQueue[i] = streamQueue[i+1];
564         }
565         streamQueueFree--;
566         ErrorHandler.assertExit(isReordering, "OpenSL ES: Buffer sent to remove is not in queue");
567     }
568     int GetState()
569     {
570         SLAndroidSimpleBufferQueueState res;
571         (*playerAndroidSimpleBufferQueue).GetState(playerAndroidSimpleBufferQueue, &res);
572 
573         return res.count; //If 0, nothing is playing
574     }
575 
576     /**
577     *   Same behavior from (*androidBufferQueue).Enqueue. If you wish to use queue
578     *   for streaming sound, call pushBuffer
579     */
580     static void Enqueue(ref SLIAudioPlayer audioPlayer, void* samples, size_t sampleSize)
581     {
582         assert(sampleSize <= uint.max, "Probably something bad will happen with that size.");
583 
584         audioPlayer.enqueued = EnqueuedBuffer(samples, sampleSize);
585 
586         (*audioPlayer.playerAndroidSimpleBufferQueue)
587             .Enqueue(audioPlayer.playerAndroidSimpleBufferQueue, samples, cast(uint)sampleSize);
588     }
589     static void Clear(ref SLIAudioPlayer audioPlayer)
590     {
591         (*audioPlayer.playerAndroidSimpleBufferQueue)
592             .Clear(audioPlayer.playerAndroidSimpleBufferQueue);
593     }
594 
595     /**
596     *   Will put the processed buffer into the free list |b| -> /b\ 
597     *
598     * - | | = Enqueued
599     *
600     * - / \ = Free
601     *
602     * - ( ) = Unused (capacity)
603     */
604     void unqueue(SLIBuffer* processedBuffer)
605     {
606         bool isReordering = false;
607         for(int i = 0; i < streamQueueLength;i++)
608         {
609             if(streamQueue[i] == processedBuffer)
610                 isReordering = true;
611             if(isReordering && i+1 < streamQueueCapacity)
612                 streamQueue[i] = streamQueue[i+1];
613         }
614         processedBuffer.hasBeenProcessed = true;
615         processedBuffer.isLocked = false;
616         streamQueueLength--;
617         streamQueue[streamQueueLength+streamQueueFree] = processedBuffer;
618         streamQueueFree++;
619         ErrorHandler.assertExit(isReordering, "SLES Error: buffer not found when trying to unqueue it");
620     }
621 
622     static void resume(ref SLIAudioPlayer audioPlayer)
623     {
624         with(audioPlayer)
625         {
626             isPlaying = true;
627             uint playState;
628             (*player).GetPlayState(player, &playState);
629 
630             if(playState == SL_PLAYSTATE_PAUSED || playState == SL_PLAYSTATE_STOPPED)
631                 (*player).SetPlayState(player, SL_PLAYSTATE_PLAYING);
632         }
633     }
634 
635     static void play(ref SLIAudioPlayer audioPlayer)
636     {
637         with(audioPlayer)
638         {
639             SLIAudioPlayer.resume(audioPlayer);
640             totalChunksEnqueued = 0;
641             totalChunksPlayed = 0;
642             hasFinishedTrack = false;
643         }
644     }
645     static void stop(ref SLIAudioPlayer audioPlayer)
646     {
647         with(audioPlayer)
648         {
649             (*player).SetPlayState(player, SL_PLAYSTATE_STOPPED);
650             SLIAudioPlayer.Clear(audioPlayer);
651             isPlaying = false;
652         }
653     }
654 
655     static void pause(ref SLIAudioPlayer audioPlayer)
656     {
657         with(audioPlayer)
658         {
659             (*player).SetPlayState(player, SL_PLAYSTATE_PAUSED);
660             isPlaying = false;
661         }
662     }
663 
664     static void seekClipPosition(ref SLIAudioPlayer audioPlayer, float posMillis, SLuint32 seekMode = SL_SEEKMODE_FAST)
665     {
666         with(audioPlayer)
667         {
668             (*playerSeek).SetPosition(playerSeek, cast(SLmillisecond)posMillis, seekMode);
669         }
670     }
671 
672     // static void setLoop(ref SLIAudioPlayer audioPlayer, bool shouldLoop, float loopStartMillis = 0, float loopEnd = -1)
673     // {
674     //     with(audioPlayer)
675     //     {
676     //         if(playerSeek is null)
677     //             return;
678     //         SLmillisecond end = cast(SLmillisecond)loopEnd;
679     //         if(loopEnd <= 0)
680     //             end = SL_TIME_UNKNOWN;
681     //         (*playerSeek).SetLoop(playerSeek, shouldLoop, cast(SLmillisecond)loopStartMillis, end);
682     //     }
683     // }
684 
685     static void setLoop(ref SLIAudioPlayer audioPlayer, bool shouldLoop)
686     {
687         audioPlayer.isLooping = shouldLoop;
688     }
689     
690 }
691 
692 /**
693 *   Returns null on failure.
694 */
695 SLIAudioPlayer* sliGenAudioPlayer(SLDataSource src,SLDataSink dest, bool autoRegisterCallback = true)
696 {
697     SLIAudioPlayer temp;
698     bool willUseFastMixer = engine.willUseFastMixer;
699     with(temp)
700     {
701         const(SLInterfaceID)[] ids = getAudioPlayerInterfaces(willUseFastMixer);
702         SLboolean[] req = getAudioPlayerRequirements(ids);
703 
704 
705         
706         version(OpenSLES1) //Used for SDK < 21. But I won't support unless someone ask.
707         {
708             sliCall(engine.CreateAudioPlayer(&playerObj, &src, &dest,
709             cast(uint)(ids.length), (cast(SLInterfaceID[])ids).ptr, req.ptr),
710             "Could not create AudioPlayer with format: "~to!string(*(cast(SLDataFormat_PCM*)src.pFormat)));
711         }
712         else
713         {
714             sliCall(engine.CreateAudioPlayer(&playerObj, &src, &dest,
715             cast(uint)(ids.length), (cast(SLInterfaceID[])ids).ptr, req.ptr),
716             "Could not create AudioPlayer with format: "~to!string(*(cast(SLAndroidDataFormat_PCM_EX*)src.pFormat)));
717         }
718 
719         sliCall((*playerObj).Realize(playerObj, SL_BOOLEAN_FALSE),
720         "Could not initialize AudioPlayer");
721 
722 
723         sliCall((*playerObj).GetInterface(playerObj, SL_IID_PLAY, &player),
724         "Could not get play interface for AudioPlayer");
725         sliCall((*playerObj).GetInterface(playerObj, SL_IID_VOLUME, &playerVol),
726         "Could not get volume interface for AudioPlayer");
727         
728 
729         if(!willUseFastMixer)
730         {
731             
732             // sliCall((*playerObj).GetInterface(playerObj, SL_IID_SEEK, &playerSeek),
733             // "Could not get Seek interface for AudioPlayer");
734 
735             //Metadata
736             sliCall((*playerObj).GetInterface(playerObj, SL_IID_METADATAEXTRACTION, &playerMetadata),
737             "Could not get MetadataExtraction interface for AudioPlayer");
738 
739             //Misc
740             sliCall((*playerObj).GetInterface(playerObj, SL_IID_PLAYBACKRATE, &playbackRate),
741             "Could not get PlaybackRate interface for AudioPlayer");
742             sliCall((*playerObj).GetInterface(playerObj, SL_IID_VIRTUALIZER, &virtualizer),
743             "Could not get Virtualizer interface for AudioPlayer");
744 
745             //Wave amplitude modifiers
746             sliCall((*playerObj).GetInterface(playerObj, SL_IID_BASSBOOST, &bassBoost),
747             "Could not get BassBoost interface for AudioPlayer");
748 
749             sliCall((*playerObj).GetInterface(playerObj, SL_IID_EQUALIZER, &equalizer),
750             "Could not get Equalizer interface for AudioPlayer");
751 
752             //Reverb
753             sliCall((*playerObj).GetInterface(playerObj, SL_IID_ENVIRONMENTALREVERB, &envReverb),
754             "Could not get EnvironmentalReverb interface for AudioPlayer");
755             sliCall((*playerObj).GetInterface(playerObj, SL_IID_PRESETREVERB, &presetReverb),
756             "Could not get PresetReverb interface for AudioPlayer");
757 
758             //Effect
759             sliCall((*playerObj).GetInterface(playerObj, SL_IID_EFFECTSEND, &playerEffectSend),
760             "Could not get EffectSend interface for AudioPlayer");
761             sliCall((*playerObj).GetInterface(playerObj, SL_IID_ANDROIDEFFECT, &androidEffect),
762             "Could not get AndroidEffect interface for AudioPlayer");
763 
764             
765             sliCall((*playerObj).GetInterface(playerObj, SL_IID_ANDROIDEFFECTSEND, &androidEffectSend),
766             "Could not get AndroidEffectSend interface for AudioPlayer");
767         }
768         
769         version(Android)
770         {
771             sliCall((*playerObj).GetInterface(playerObj, 
772             SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerAndroidSimpleBufferQueue),
773             "Could not get AndroidSimpleBufferQueue for AudioPlayer");
774         }
775     }
776     if(sliErrorMessages.length == 0)
777     {
778         SLIAudioPlayer* playerOut = new SLIAudioPlayer();
779         *playerOut = temp;
780         if(autoRegisterCallback)
781         {
782             temp.RegisterCallback(&SLIAudioPlayer.checkStreamCallback, cast(void*)playerOut);
783             temp.SetCallbackEventsMask(SL_PLAYEVENT_HEADATEND);
784         }
785         genPlayers~= playerOut;
786         return playerOut;
787     }
788     return null;
789 }
790 
791 void sliDestroyContext()
792 {
793     import core.stdc.stdlib;
794     engine.Destroy();
795     with(outputMix) (*outputMixObj).Destroy(outputMixObj);
796 
797     foreach(ref gp; genPlayers)
798     {
799         SLIAudioPlayer.destroyAudioPlayer(*gp);
800         destroy(gp);
801         gp = null;
802     }
803 }
804 
805 
806 
807 bool sliCreateOutputContext(
808     bool hasProAudio=false,
809     bool hasLowLatencyAudio=false,
810     int  optimalBufferSize=4096,
811     int  optimalSampleRate=44_100,
812     bool willUseFastMixer = false
813 )
814 {
815     engine = SLIEngine(null,null,null, willUseFastMixer);
816     engine.initialize();
817 
818     // loadSawtooth();
819 
820     SLIOutputMix.initializeForAndroid(outputMix, engine, willUseFastMixer);
821     // SLIAudioPlayer.initializeForAndroid(gAudioPlayer, engine, src, destination);
822     
823     return sliErrorQueue.length == 0;
824 }